package cn.bonoon.kernel.support.services;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import cn.bonoon.entities.LogEntity;
import cn.bonoon.kernel.query.Pageable;
import cn.bonoon.kernel.support.IOperator;
import cn.bonoon.kernel.support.ProxyEntityManager;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.support.models.Page;
import cn.bonoon.kernel.support.searcher.ISearcher;
import cn.bonoon.kernel.support.searcher.StandardSearcher;

/**
 * 比较底层的一个服务接口
 * @author jackson
 *
 * @param <E>
 */
public abstract class ServiceSupport implements ApplicationContextAware, InitializingBean, ProxyEntityManager{

	private static final Map<Class<?>, ISearcher> searcherMapped = new ConcurrentHashMap<>();
	
	/**
	 * 这个接口应该是可以被替换掉的
	 */
	protected EntityManager entityManager;
	
	protected ApplicationContext applicationContext;
	
	protected ServiceSupport() { }
	
	@Override
	public void afterPropertiesSet() throws Exception { }
	
	/**
	 * 根据类型<code>entityClass</code>进行{@link ISearcher}的注册
	 * @param applicationContext
	 * @param entityClass
	 * @return
	 */
	protected ISearcher _register(Class<? extends Persistable> entityClass){
		ISearcher searcher = searcherMapped.get(entityClass);
		if(null == searcher){
			searcher = new StandardSearcher(entityClass, applicationContext);
			searcherMapped.put(entityClass, searcher);
		}
		return searcher;
	}
	
	/** 子类可以通过这个方法向用户日志里添加一条日志记录 */
	protected void userLog(IOperator user, String content){
		LogEntity log = new LogEntity();
		log.setContent(content);
		log.setCreateAt(new Date());
		log.setCreatorId(user.getId());
		log.setLoginName(user.getUsername());
		log.setOwnerId(user.getOwnerId());
		log.setUserName(user.getDisplayName());
		entityManager.persist(log);
	}
	
	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		applicationContext = context;
	}
	
	@Override
	public <I> List<I> resultList(Class<I> iClass, String ql, Iterable<Object> parameters) {
		TypedQuery<I> query = entityManager.createQuery(ql, iClass);
		__query(query, parameters);
		return query.getResultList();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public List<Object> resultList(String ql, Iterable<Object> parameters) {
		Query query = entityManager.createQuery(ql);
		__query(query, parameters);
		return query.getResultList();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public Page resultPage(String sql, String cql, Pageable pageable, Iterable<Object> parameters) {
		Query query = entityManager.createQuery(sql), count = entityManager.createQuery(cql);
		int i = 1;
		for(Object par : parameters){
			count.setParameter(i, par);
			query.setParameter(i++, par);
		}
		query.setFirstResult(pageable.getFirstItem());
		query.setMaxResults(pageable.getPageSize());
//		List list = query.getResultList();
		return new Page(((Number)count.getSingleResult()).longValue(), query.getResultList());
	}
	
	protected void __query(Query query, Iterable<Object> parameters){
		int i = 1;
		for(Object par : parameters){
			query.setParameter(i++, par);
		}
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}
	
	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql) {
		return entityManager.createQuery(hql, clazz);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object arg) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		return tqSelect.setParameter(1, arg);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object arg, Object arg2) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		return tqSelect.setParameter(1, arg).setParameter(2, arg2);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object... args) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		for (int i = 0, l = args.length; i < l; i++) {
			tqSelect.setParameter(i + 1, args[i]);
		}
		return tqSelect;
	}

	//---------------------------------exsit
	protected boolean __exsit(String ql){
		return __exsit(__query(Number.class, ql));
	}
	
	protected boolean __exsit(String ql, Object arg){
		return __exsit(__query(Number.class, ql, arg));
	}
	
	protected boolean __exsit(String ql, Object arg, Object arg2){
		return __exsit(__query(Number.class, ql, arg, arg2));
	}
	
	protected boolean __exsit(String ql, Object... args){
		return __exsit(__query(Number.class, ql, args));
	}
	
	protected boolean __exsit(TypedQuery<Number> t){
		return t.getSingleResult().intValue() > 0;
	}

	//---------------------------------first
	protected <T> T __first(TypedQuery<T> tqSelect) {
		List<T> items = tqSelect.setMaxResults(1).getResultList();
		return items.isEmpty() ? null : items.get(0);
	}

	protected <T> T __first(Class<T> clazz, String hql) {
		return __first(__query(clazz, hql));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object arg) {
		return __first(__query(clazz, hql, arg));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object arg, Object arg2) {
		return __first(__query(clazz, hql, arg, arg2));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object... args) {
		return __first(__query(clazz, hql, args));
	}

	//---------------------------------single
//	protected <T> T __single(Class<T> clazz, String hql) {
//		return __query(clazz, hql).getSingleResult();
//	}
//
//	protected <T> T __single(Class<T> clazz, String hql, Object arg) {
//		return __query(clazz, hql, arg).getSingleResult();
//	}
//
//	protected <T> T __single(Class<T> clazz, String hql, Object arg, Object arg2) {
//		return __query(clazz, hql, arg, arg2).getSingleResult();
//	}

	protected <T> T __single(Class<T> clazz, String hql, Object... args) {
		return __query(clazz, hql, args).getSingleResult();
	}

	//---------------------------------list
	protected <T> List<T> __list(Class<T> clazz, String hql) {
		return __query(clazz, hql).getResultList();
	}

	protected <T> List<T> __list(Class<T> clazz, String hql, Object arg) {
		return __query(clazz, hql, arg).getResultList();
	}

	protected <T> List<T> __list(Class<T> clazz, String hql, Object arg, Object arg2) {
		return __query(clazz, hql, arg, arg2).getResultList();
	}
	
	protected <T> List<T> __list(Class<T> clazz, String hql, Object... args) {
		return __query(clazz, hql, args).getResultList();
	}
	
//---------------------------------top
	protected <T> List<T> __top(int top, Class<T> clazz, String hql) {
		return __top(top, __query(clazz, hql));
	}

	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object arg) {
		return __top(top, __query(clazz, hql, arg));
	}

	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object arg, Object arg2) {
		return __top(top, __query(clazz, hql, arg, arg2));
	}
	
	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object... args) {
		return __top(top, __query(clazz, hql, args));
	}
	
	protected <T> List<T> __top(int top, TypedQuery<T> query){
		if(top > 0) query.setMaxResults(top);
		return query.getResultList();
	}

//	//---------------------------------page
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql) {
//		return __query(clazz, hql).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object arg) {
//		return __query(clazz, hql, arg).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object arg, Object arg2) {
//		return __query(clazz, hql, arg, arg2).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//	
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object... args) {
//		return __query(clazz, hql, args).setFirstResult(skip).setMaxResults(size).getResultList();
//	}

	//---------------------------------exec
	protected int __exec(String ql){
		return entityManager.createQuery(ql).executeUpdate();
	}

	protected int __exec(String ql, Object arg) {
		return entityManager.createQuery(ql).setParameter(1, arg).executeUpdate();
	}

	protected int __exec(String ql, Object arg, Object arg2) {
		return entityManager.createQuery(ql).setParameter(1, arg).setParameter(2, arg2).executeUpdate();
	}

	protected int __exec(String ql, Object arg, Object arg2, Object arg3) {
		return entityManager.createQuery(ql)
				.setParameter(1, arg)
				.setParameter(2, arg2)
				.setParameter(3, arg3).executeUpdate();
	}
	
	/**
	 * loadgraph：在原有Entity的定义的基础上，定义还需要获取什么字段/关系
	 * @param clazz
	 * @param hql
	 * @param notLazyAttrs 还需要获取什么字段/关系(必须是ｆｅｔｃｈ＝ｌａｚｙ)
	 * @param args
	 * @return
	 */
	protected <T> List<T> __listWithLoadgraph(Class<T> clazz, String hql,List<String> notLazyAttrs,List<Object> paramer) {
		return this.__listWithHint("javax.persistence.loadgraph", clazz, hql, notLazyAttrs, paramer);
	}
	/**
	 * loadgraph：在原有Entity的定义的基础上，定义还需要获取什么字段/关系
	 * @param clazz
	 * @param hql
	 * @param notLazyAttrs 还需要获取什么字段/关系(必须是ｆｅｔｃｈ＝ｌａｚｙ)
	 * @return
	 */
	protected <T> List<T> __listWithLoadgraph(Class<T> clazz, String hql,List<String> notLazyAttrs) {
		return this.__listWithHint("javax.persistence.loadgraph", clazz, hql, notLazyAttrs,null);
	}
	protected <T> List<T> __listWithLoadgraph(Class<T> clazz, String hql,List<String> notLazyAttrs,Object arg) {
		List<Object> obj=new ArrayList<>();
		obj.add(arg);
		return this.__listWithHint("javax.persistence.loadgraph", clazz, hql, notLazyAttrs,obj);
	}
	protected <T> List<T> __listWithLoadgraph(Class<T> clazz, String hql,List<String> notLazyAttrs,Object arg,Object arg1) {
		List<Object> obj=new ArrayList<>();
		obj.add(arg);
		obj.add(arg1);
		return this.__listWithHint("javax.persistence.loadgraph", clazz, hql, notLazyAttrs,obj);
	}
	protected <T> List<T> __listWithLoadgraph(Class<T> clazz, String hql,List<String> notLazyAttrs,Object arg,Object arg1,Object arg2) {
		List<Object> obj=new ArrayList<>();
		obj.add(arg);
		obj.add(arg1);
		obj.add(arg2);
		return this.__listWithHint("javax.persistence.loadgraph", clazz, hql, notLazyAttrs,obj);
	}
	/**
	 * hit：在原有Entity的定义的基础上，定义还需要获取什么字段/关系
	 * @param clazz
	 * @param hql
	 * @param notLazyAttrs 还需要获取什么字段/关系
	 * @param args
	 * @return
	 */
	protected <T> List<T> __listWithHint(String hintName,Class<T> clazz, String hql,Iterable<String> notLazyAttrs,List<Object> paramer) {
		EntityGraph<T> graph = entityManager.createEntityGraph(clazz);
		for(String attr : notLazyAttrs) {
			graph.addSubgraph(attr);
		}
		
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz).setHint(hintName, graph);
		if(null!=paramer){
			for (int i = 0;i < paramer.size(); i++) {//, l = args.length;
				tqSelect.setParameter(i + 1, paramer.get(i));
			}
		}
		return tqSelect.getResultList();
	}
	
	/**
	 * fetchgraph：完全放弃原有Entity的定义，定义仅需要获取什么字段/关系
	 * @param clazz
	 * @param hql
	 * @param notLazyAttrs 还需要获取什么字段/关系
	 * @param args
	 * @return
	 */
	protected <T> List<T> __listWithFetchgraph(Class<T> clazz, String hql,Iterable<String> notLazyAttrs,List<Object> parmer) {
		return this.__listWithHint("javax.persistence.fetchgraph", clazz, hql, notLazyAttrs, parmer);
	}
	
	interface WriteDB{
		void execute(Object o);
	}
	
	
	public final static class BatchCommitEntityManager {
		private static final int _BATH_SIZE = 1000;
		public BatchCommitEntityManager(EntityManager entityManager){
			this.entityManager = entityManager;
		}
		final EntityManager entityManager;
		private final List persistObjs = new ArrayList();
		private final List mergeObjs = new ArrayList();
		private final List removeObjs = new ArrayList();
		public EntityManager getEntityManager(){
			
			return this.entityManager;
		}
		
		public BatchCommitEntityManager persist(Object entity) {
			persistObjs.add(entity);
			return this;
		}

		public BatchCommitEntityManager merge(Object entity) {
			mergeObjs.add(entity);
			return this;
		}
		
		public BatchCommitEntityManager merge(List objs) {
			mergeObjs.addAll(objs);
			return this;
		}

		public void remove(Object entity) {
			removeObjs.add(entity);	
		}
		/**
		 * 有添加的先执行添加
		 */
		public void insertNew(){
			// 新增的
			excuteBatch(persistObjs,new WriteDB(){
				@Override
				public void execute(Object o) {
					entityManager.persist(o);
				}
			});
			persistObjs.clear();
		}
		
		/**
		 * 批量执行新增，更新和删除任务．
		 */
		public void excuteBatch(){
			// 新增的
			excuteBatch(persistObjs,new WriteDB(){
				@Override
				public void execute(Object o) {
					entityManager.persist(o);
				}
			});
			
			excuteBatch(mergeObjs,new WriteDB(){
				@Override
				public void execute(Object o) {
					entityManager.merge(o);
				}
			});
			
			excuteBatch(removeObjs,new WriteDB(){
				@Override
				public void execute(Object o) {
					entityManager.remove(o);
				}
			});
		}
		
		private void excuteBatch(List objs,WriteDB method){
			for(int i =0; i < objs.size();i++){
				method.execute(objs.get(i));
				if (i % _BATH_SIZE == 0 || i==(objs.size()-1)) { // 每1000条数据执行一次，或者最后不足1000条时执行  
					entityManager.flush();  
					entityManager.clear();  
	            }  
			}
			//objs.clear();
		}

	}
}
